package server;

import iface.*;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * The StubDatabase imitates that Database class. Main difference is that the
 * stub is not persistent.
 * 
 * @author Terry
 */

public class StubDatabase implements ClientQueryable {

    private static StubDatabase instance = null;
    boolean conn = true; // true if there is a connection
    String db = "account_db";
    List<HvZUser> al = new ArrayList<HvZUser>();

    // StubDB Constructor
    private StubDatabase() {
        Logger.log("STUB DATABASE: A stub database instance has been created.");
    }

    /**
     * @return a StubDatabase instance
     */
    public static StubDatabase getInstance() {
        if (instance == null)
            instance = new StubDatabase();
        return instance;
    }

    /**
     * @return a list of humans
     */
    public Status getHumansList() {
        return getUserList(UserTypes.HUMAN);
    }

    /**
     * @return a list of zombies
     */
    public Status getZombiesList() {
        return getUserList(UserTypes.ZOMBIE);
    }

    /**
     * @return a list of observers
     */
    public Status getObserversList() {
        return getUserList(UserTypes.OBSERVER);
    }

    // Returns a list of users
    private synchronized Status getUserList(UserTypes type) {
        if (!conn)
            return null;
        List<HvZUser> ll = new LinkedList<HvZUser>();

        for (int i = 0; i < al.size(); i++) {
            if (al.get(i).getUserType() == type)
                ll.add(al.get(i));
        }

        Status status = new Status(Status.Result.SUCCESS,
                Message.MsgType.LOCATION);
        status.setLocationList(ll);
        return status;
    }

    /**
     * Registers a new user
     * 
     * @param user
     *            the user to register
     * @return a status indicating success or error
     */
    public synchronized Status registerNewUser(Registration user) {
        if (!conn)
            return null;
        if (user == null || user.getUser() == null || user.getPhoneNo() == null) {
            Status s = new Status(Status.Result.ERROR,
                    Message.MsgType.REGISTRATION);
            s.setMessage("A registration error has occured");
            return s;
        } else if (user.getUser().length() > 32) {
            Status s = new Status(Status.Result.ERROR,
                    Message.MsgType.REGISTRATION);
            s.setMessage("The username was too long");
            return s;
        }

        HvZUser u = findUser(user.getUser(), user.getPhoneNo());
        if (u != null) {
            Status status = new Status(Status.Result.ERROR,
                    Message.MsgType.REGISTRATION);
            boolean dupName = u.getUsername().equals(user.getUser());
            boolean dupPhone = u.getPhone().equals(user.getPhoneNo());

            if (dupName && dupPhone)
                status.setMessage("Duplicate username and phone number");
            else if (dupName)
                status.setMessage("Duplicate username");
            else
                status.setMessage("Duplicate phone number");

            return status;
        }

        UserTypes type = user.getUserType();

        al.add(new HvZUser(user.getUser(), type, user.getPhoneNo()));
        Status status = new Status(Status.Result.SUCCESS,
                Message.MsgType.REGISTRATION);
        status.setMessage("Successful registration");
        return status;
    }

    /**
     * Kills a user
     * 
     * @param username
     *            the user to change
     * @return a status indicating success or failure
     */
    public synchronized Status changeToZombie(String username) {
        if (!conn)
            return null;

        HvZUser u = findUser(username, null);
        if (u == null) {
            Status status = new Status(Status.Result.ERROR,
                    Message.MsgType.KILL);
            status.setMessage("No such user");
            return status;
        } else if (u.getUserType() == UserTypes.OBSERVER) {
            Status status = new Status(Status.Result.ERROR,
                    Message.MsgType.KILL);
            status.setMessage("Observers cannot be killed.");
            return status;
        } else if (u.isZombie()) {
            Status status = new Status(Status.Result.ERROR,
                    Message.MsgType.KILL);
            status.setMessage("The user is already a zombie");
            return status;
        }

        Status status = new Status(Status.Result.SUCCESS, Message.MsgType.KILL);
        u.killUser();
        return status;
    }

    /**
     * Sets the Database to use. For the stub setting it to null will simulate a
     * connection error
     * 
     * @param db
     *            the database name
     */
    public synchronized void setDatabase(String db) {
        conn = (db != null);
        this.db = db;
    }

    /**
     * @return the database name
     */
    public synchronized String getDatabase() {
        return db;
    }

    /**
     * Empties the contents of the DB.
     */
    public synchronized void clearDatabase() {
        al.clear();
    }

    // Finds and returns user. Returns null if user not found. Helps enforce
    // unique username/phone
    private synchronized HvZUser findUser(String username, String phone) {
        for (int i = 0; i < al.size(); i++) {
            HvZUser user = al.get(i);
            if (user.getUsername().equals(username)
                    || user.getPhone().equals(phone)) {
                return user;
            }
        }
        return null;
    }

    /**
     * @return true if user exists, false otherwise
     */
    public synchronized boolean doesUserExist(String username) {
        for (int i = 0; i < al.size(); i++) {
            if (al.get(i).getUsername().equals(username))
                return true;
        }
        return false;
    }

    /**
     * Finds and returns a user if it found, null if not.
     */
    public synchronized HvZUser getUser(String username) {
        for (int i = 0; i < al.size(); i++) {
            if (al.get(i).getUsername().equals(username))
                return al.get(i);
        }
        return null;
    }

}
